# -*-coding:utf-8 -*-
"""
主程序模块 这是一个示例程序包
主窗体类 main

# 1、删除原安装的模块【用虚拟环境太慢，没删除重装快】
pip freeze > python_modules.txt
pip uninstall -r python_modules.txt -y

# 2、模块安装【本程序所需的模块】
pip install Pillow -i https://pypi.douban.com/simple
pip install chardet -i https://pypi.douban.com/simple

# 3、封装命令【注意修改 D:\xxx\ 为程序保存位置】

pyinstaller --onefile --clean --noupx --noconsole --icon = "D:\xxx\example\Images\icon.ico" D:\xxx\example\main.py

"""

#■■■■■■■■■■■■■■■■■■ 调用模块
import tkinter
from tkinter import ttk 
import os    # 系统
from tkinter import filedialog #打开系统文件对话框
import tkinter.messagebox #系统消息框


#■■■■■■■■■■■■■■■■■■ 调用自定义模块
import about            # 关于窗口模块
import publicMethods    # 公共方法、函数定义模块
import ToolTip          # 命令提示模块
import setup            # 设置窗口模块

#■■■■■■■■■■■■■■■■■■声明公共常用

from constant import STR_SOFTWARE_NAME              #软件名称
from constant import STR_VERSION_NUMBER             #工具版本号
from constant import SRE_CONVER_CODE                #默认文本编码
from constant import STR_SELF_PATH , STR_IMAGES_DIR #获取当前软件目录、图片目录
from constant import STR_CREATE_DB_TABLE_SETUP      # 创建数据库
#■■■■■■■■■■■■■■■■■■仅在本模块中用到的常用

INT_MAIN_WINDOW_WIDTH        = 500 #默认窗体宽度
INT_MAIN_WINDOW_HEIGHT       = 350 #默认窗体高度

STRS_BUTTON01_GIF_IMAGES=[['eye1.png',3000],  # 3 秒眨一下眼
                          ['eye2.png',100], 
                          ['eye3.png',100], 
                          ['eye2.png',100]]  # 按键动态图片文件名，后面数值表示显示的时间【毫秒】

STRS_BUTTON02_GIF_IMAGES=[['run1.png',300], 
                          ['run2.png',300], 
                          ['run3.png',300]]  # 按键动态图片文件名，后面数值表示显示的时间【毫秒】

#============小部件提示信息
STR_COMMAND_NOTE_OPEN = "打开文本文件"
STR_COMMAND_NOTE_SAVE = "保存编辑内容"
STR_COMMAND_NOTE_SETUP = "打开软件设置窗口"
STR_COMMAND_NOTE_ABOUT = "打开关于窗口"

#■■■■■■■■■■■■■■■■■■定义类内容

class main(tkinter.Tk):

    # 成员变量
    button1_index=0     # 动态按键 1 显示图片的序号
    button2_index=0     # 动态按键 1 显示图片的序号
    tipTime = 8         # 默认命令提示时间
    filepath = ''       # 打开的文件路径
    isEdited  = False   #是否编辑过，为 True 时表示文本框中内容已修改过

    #■■■■■■■■■■■■■■■■■■ 初始化方法
    def __init__(self): #构造函数
        tkinter.Tk.__init__(self)

        #========================初始化成员变量

        # ■■■■■■■■ 窗口在屏幕居中显示
        x = int((self.winfo_screenwidth()-INT_MAIN_WINDOW_WIDTH)/2) #子窗口开始x坐标
        y = int((self.winfo_screenheight()-INT_MAIN_WINDOW_HEIGHT)/2) #子窗口开始y坐标

        self.protocol('WM_DELETE_WINDOW' , self.colse)#关闭窗口后的事件：colse

        self.geometry('{}x{}+{}+{}'.format(INT_MAIN_WINDOW_WIDTH , INT_MAIN_WINDOW_HEIGHT , x , y)) #窗口定位
        #self.geometry('+{}+{}'.format(x , y)) #窗口定位（不指定窗口尺寸，由 self.initUI() 方法中小部件决定）
        #self.minsize(INT_MAIN_WINDOW_WIDTH , INT_MAIN_WINDOW_HEIGHT) #设定窗口最小尺寸（不指定窗口尺寸时建议使用本句）
        self.resizable(False , False)  #不得改变尺寸

        self.title("{}    ——{}".format(STR_SOFTWARE_NAME , STR_VERSION_NUMBER)) #窗口标题
        self.iconbitmap(os.path.join(STR_SELF_PATH  ,  STR_IMAGES_DIR  ,  'icon.ico'))
        
        # =  =  =  =  =  =  =  =  =  = 窗体界面 GUI 初始化
        self.loadImages()#加载图标
        self.initUI() #初始化窗体框架

        self.show() #显示窗口

        #
    def initUI(self):#初始化窗体框架
        
        toolbar = tkinter.Frame(self)
        toolbar.pack( side = tkinter.TOP , fill = tkinter.X , padx = 5 , pady = 3 ) # 放置工具栏容器

        self.initUI_toolbar(toolbar) #定义工具伴按键

        content = tkinter.Frame(self)
        content.pack( side = tkinter.TOP , fill = tkinter.BOTH , expand = True , padx=3 , pady=2 ) # 放置窗口主要容器

        self.widget_textMain=publicMethods.createScrollbarWidget(content , type=1 , height = 13 , width = 100)  # 生成带滚动条的文本框

        self.widget_textStatebar = tkinter.Text( self , height = 2 , state = tkinter.DISABLED , #不可编辑
                                                  bg = 'SystemButtonFace' , borderwidth = 2 , relief = "ridge" , font = ("" , 9) )
        self.widget_textStatebar.pack( side = tkinter.TOP , fill = tkinter.X , padx = 1 , pady = 1 ) # 定义提示栏

        self.widget_textStatebar.bind("<KeyPress>" , self.onKeyboard)  # 键盘事件
        #
    def initUI_toolbar(self , frame):#定义工具伴按键

        padx=1  # 按键之间隔宽度

        self.widget_button1=tkinter.Button(frame , image=self.imgButtons1[0] , command= self.openTextFile) #按键 1 【眼睛】 ，后面要修改其图片，因此使用成员变量
        self.widget_button1.pack(side=tkinter.LEFT , padx=padx)

        self.widget_button2=tkinter.Button(frame , image=self.imgButtons2[0] , command=self.saveTextFile) # 按键 2 【运行】 ，后面要修改其图片，因此使用成员变量
        self.widget_button2.pack(side=tkinter.LEFT , padx=padx)

        tkinter.Label(frame).pack(side=tkinter.LEFT , padx=0)    # 间隔 , 可用 padx 微调距离

        butSetup=tkinter.Button(frame , image=self.imgSetup , command=self.openSetup) # 按键 3 【设置】
        butSetup.pack(side=tkinter.LEFT , padx=padx)
        
        butAbout=tkinter.Button(frame , image=self.imgAbout , command= self.openAbout) # 按键 3 【关于】
        butAbout.pack(side=tkinter.LEFT , padx=padx)

        self.createToolTip(self.widget_button1  , STR_COMMAND_NOTE_OPEN)
        self.createToolTip(self.widget_button2  , STR_COMMAND_NOTE_SAVE)
        self.createToolTip(butSetup             , STR_COMMAND_NOTE_SETUP)
        self.createToolTip(butAbout             , STR_COMMAND_NOTE_ABOUT)

        #
    def init_datas(self):#窗口小部件的初始化
        publicMethods.setDatasFromSQL(STR_CREATE_DB_TABLE_SETUP) #数据库不存在时创建

        self.setButtonImage1() #按键1 开始循环播放图片
        self.setButtonImage2() #按键2 开始循环播放图片

        self.tipTime=int(publicMethods.getDatabSetup('tiptime',8))  #从数据库中提取命令提示时间，注意要转换为整型

        #
    #■■■■■■■■■■■■■■■■■■ 类基本方法
    def show(self):#显示窗体
        
        self.init_datas() #窗口小部件的初始化

        self.mainloop() #显示窗体

        #
    def loadImages(self):#预先加载图标
        # 图片必须保存为成员变量或全局变量才可以正常显示

        self.imgSetup = publicMethods.loadImage("setup.ico")
        self.imgAbout = publicMethods.loadImage("about.png")

        self.imgButtons1 = []

        for img in STRS_BUTTON01_GIF_IMAGES:
            self.imgButtons1.append(publicMethods.loadImage(img[0])) # 加载图片组
        

        self.imgButtons2 = []

        for img in STRS_BUTTON02_GIF_IMAGES:
            self.imgButtons2.append(publicMethods.loadImage(img[0])) # 加载图片组
        
        #
    def colse(self):#关闭窗口事件
        if self.isEdited:
            if tkinter.messagebox.askokcancel('退出软件','确定要放弃修改退出软件吗？' , parent=self) != True:
                return
        
        self.destroy()

        #
    def setButtonImage1(self):# 实现按键1动画方法【 自动调用自身实现动画 】

        self.button1_index += 1
        if self.button1_index == len(STRS_BUTTON01_GIF_IMAGES):
            self.button1_index = 0

        self.widget_button1.config(image = self.imgButtons1[self.button1_index] )  #切换下一张图片

        self.after( STRS_BUTTON01_GIF_IMAGES[self.button1_index][1] ,  # 取切换图片的时间
                    self.setButtonImage1 ) # 调用自身
    
        #
    def setButtonImage2(self):# 实现按键1动画方法【 自动调用自身实现动画 】

        self.button2_index += 1
        if self.button2_index == len(STRS_BUTTON02_GIF_IMAGES):
            self.button2_index = 0

        self.widget_button2.config(image = self.imgButtons2[self.button2_index] )  #切换下一张图片

        self.after( STRS_BUTTON02_GIF_IMAGES[self.button2_index][1] ,  # 取切换图片的时间
                    self.setButtonImage2 ) # 调用自身
    
        #
    def createToolTip(self , widget , text):#创建 ToolTip 即显示组件提示文本
        
        toolTip = ToolTip.ToolTip(widget) #注意显示时间
        def enter(event):
            toolTip.setTime(self.tipTime * 1000)
            toolTip.showtip(text)

        def leave(event):
            toolTip.hidetip()

        widget.bind('<Enter>', enter)
        widget.bind('<Leave>', leave)
    def showStateInfo(self , text , clear=1): # 在状态栏显示信息， clear =0 时，不清除原内容
        self.widget_textStatebar.config(state=tkinter.NORMAL) #解除禁用状态

        if clear:
            self.widget_textStatebar.delete('1.0','end') # 清除原内容

        self.widget_textStatebar.insert('end',text) # 显示信息

        self.widget_textStatebar.config(state=tkinter.DISABLED) #恢复禁用状态

        #
    def setTimptime(self,time):# 设置命令提示时间（由设置模块调用）
        self.tipTime = time

        #
    #■■■■■■■■■■■■■■■■■■ 小部件事件
    def onKeyboard(self , event):#文本框键盘事件

        self.isEdited = True # 标记为文本框内容被编辑过

        #
    #■■■■■■■■■■■■■■■■■■ 操作命令
    def openTextFile(self):#打开文本文件
        file = filedialog.askopenfilename(title="打开文本文件",
                                          filetypes=(("文本文件" , "*.txt") , ("所有文件" , "*.*")),
                                          parent=self)

        if file=="" :
            return  # 取消操作
        
        self.filepath=file #保存打开的文件路径

        text = publicMethods.readTextFile(file)

        self.widget_textMain.delete("1.0" , "end") # 删除文本框原内容

        self.widget_textMain.insert("end" , text)

        self.showStateInfo("已读取“{}”文件内容并显示！".format(file))

        self.isEdited = False # 重新标记文本框未被修改过

        #
    def saveTextFile(self):#保存文本文件
        text=self.widget_textMain.get('1.0' , 'end')[:-1]  #去掉最后的换行符

        if len(text)==0: #无内容
            return

        path,file = os.path.split(self.filepath)

        file = filedialog.asksaveasfilename(title="保存文本文件" ,
                                            initialdir=path ,
                                            initialfile=file ,
                                            filetypes=(("文本文件" , "*.txt") , ("所有文件" , "*.*")),
                                            parent=self)

        if file=="" :
            return  # 取消操作
        
        if tkinter.messagebox.askyesno('保存文件','本软件非正式文件编辑器，是否确定要？',parent=self)!=True:
            return
        
        if os.path.isfile(file): # 文件存在时
            code=publicMethods.detectFileCode(file)
        else:
            code=SRE_CONVER_CODE  # 保存为新文件时使用默认编码

        if publicMethods.saveTextFile(file , text , code):
            self.showStateInfo("保存文件成功！")
            self.isEdited = False # 重新标记文本框未被修改过

        else:
            self.showStateInfo("保存文件失败！")

        #
    def openSetup(self):# 打开设置窗口

        m=setup.systemSetup(self)
        m.show()

        #if m.isOK: # 注意，只有要 systemSetup.show() 方法中采用 wait_window() 时才有意义 （但同时设置窗口就无法使用命令提示）
        #    self.tipTime = m.int_spbTiptime.get()

        #
    def openAbout(self):#打开关于窗口

        ab = about.about(self)
        ab.show()

    #


if __name__  ==  "__main__":
    # 从数据库中取设置，决定打开窗口的模式【默认为系统存储器模式】
    m = main()
